射频识别(RFID)技术

125KHz ID 卡

作者:陈广 日期:2019-1-16


简介

ID 卡全称为身份识别卡(Identification Card),是一种最简单的 RFID 卡。它不可写入,含固定的编号,主要有台湾 SYRIS 的 EM 格式、美国 HIDMOTOROLA 等各类 ID 卡。ID 卡与磁卡一样,都仅仅使用了“卡的号码”而已,卡内除了卡号外,无任何保密功能,其“卡号”是公开、裸露的。所以说 ID 卡就是“感应式磁卡”。 ISO 标准 ID 卡的规格为: 85.6x54x0.80±0.04mm(高/宽/厚),市场上也存在一些厚、薄卡或异型卡。

ID 卡系统由卡、读卡器和后台控制器组成。工作过程如下:

  • 读卡器将载波信号经天线向外发送,载波频率为 125KHZ(THRC12);
  • ID 卡进入读卡器的工作区域后,由卡中电感线圈和电容组成的谐振回路接收读卡器发射的载波信号,卡中芯片的射频接口模块由此信号产生出电源电压、复位信号及系统时钟,使芯片“激活”;
  • 芯片读取控制模块将存储器中的数据经调相编码后调制在载波上经卡内天线回送给读卡器;
  • 读卡器对接收到的卡回送信号进行解调、解码后送至后台计算机;
  • 后台计算机根据卡号的合法性,针对不同应用做出相应的处理和控制。

以上描述过于专业,我们讲通俗一点:ID 卡本身是没有电池的,在不接触的情况下读卡器如何读取卡内存储的信息呢?其实我们在中学学物理时都知道,当电磁波通过线圈时会使线圈产生电流。ID 卡内就有线圈,我们将 ID 卡对着灯光看就能看见线圈。而读卡器则发送电磁波,当 ID 卡靠近读卡器时,线圈产生电流,这样 ID 卡就有了电,然后 ID 卡通过这点电将卡的信息通过卡中的天线发送给读卡器,这样读卡器就可以读取卡中的信息了。

图 1:各种 ID 卡

ID 卡的使用非常普遍,我用到的就有:住宅楼楼下铁门的门禁卡,电单车出入卡(准备被人脸识别取代)。另外还有停车场使用它来进行身份识别(现在也基本被摄像头 AI 识别所取代)。由于其无须内置电源,使用时无接触且寿命长,因此在弱电系统中有广泛的应用。

ID卡的出现基本上淘汰了早期的磁卡或接触式 IC 卡。但由于 ID 卡不可写入用户数据,其记录内容仅限卡号只可由芯片厂一次性写入,开发商只可读出卡号加以利用。随着市场技术不断的发展,现在市场上到处都可以买到 ID 门禁卡复制机,ID 门禁卡是可以进行复制的,这在安全问题上带来了一定为威胁。相比于 ID 卡,IC 卡的安全性更高,像公交卡,里面还存有卡号、用户资料、使用权限、卡上金额等,数据的读取、写入均需要相应的密码认证,是一种双向认证的卡,相对而言,难以复制。所以业界共识是 ID 卡不可能做成一卡通,也不可能做消费,除非能接受其不足之处(如上所述)。

125KHz 读卡器

我在系里找到两种类型的 125KHz 读卡器,一种是实验箱里的,裸体读卡器,下篇文章再针对它进行介绍。另一种是单独的 125KHz 读卡器,可读取 ID 卡和 IC 卡(改天看看有没有 IC 卡,找一张来读读),如下图:

图 2:本文使用的读卡器及 ID 卡

此读卡器应当是实验箱配的,估计也是厂家从淘宝买的,居然把读卡器后面的参数说明标签全部撕掉。要使用只能自己尝试了。另外不同的读卡器读卡时输出的格式是不一样的,万幸的是我在淘宝搜到以下说明。弄懂了在读取卡中数据后如何进行转换以得到正确的 ID 卡号码。

ID卡的原始卡号是固定的,而读卡器根据原始卡号可以转换输出不同的位数和格式,各个厂家的读卡器输出不太一样,但基本上是按一定的标准输出,以下是各个厂家不同读卡器的数据输出格式。

  • 格式1:10 位十六进制的ASCII 字符串,即10 Hex 格式。如:某样卡读出十六进制卡号为:“01026f6c3a”。
  • 格式2:将格式1 中的后8 位,转换为10 位十进制卡号,即8H---10D。即将“ 026f6c3a”转换为:“0040856634”。
  • 格式3:将格式1 中的后6 位,转换为8 位十进制卡号,即6H---10D。即将“ 6f6c3a”转换为:“07302202”。
  • 格式4:将格式1 中的倒数第5、第6 位,转换为3 位十进制卡号,再将后4 位,转换为 5 位十进 制卡号,中间用“,”分开,即“2H + 4H”。即将2H“ 6f”转换为:“111”,4H “6c3a”转为“27706”。最终将2 段号连在一起输出为“111,27706”。
  • 格式5:将格式1 中后8 位的前4 位,转换为5 位十进制卡号,再将后4 位,转换为5 位十进制卡 号,中间用“,”分开,即“4Hex + 4Hec”。照此推算结果为:00623,27706 (4H+4H)。

读卡器通讯模式

此读卡器采用 UART 接口与计算机通信,UART 接口一帧的数据模式为 1 个起始位,8个数据位,无奇偶校验位,波特率为 9600 bps(刚开始我用 19200 试了半天,结果老读不出正确的 ID 卡号),其向外输出的数据格式参考下表:

卡号 校验
4 字节 1 字节

当有卡进入读卡器的射频区域时,读卡器通过 UART 接口发送上表所示格式的数据包,即 5 个字节,如果卡一直处于射频区域内,读卡器不再重复发送。卡片离开射频区域,也不会再发送任何数据。

ID 卡数据转换

以图2中的上边那张 ID 卡为例,卡上标的号码为:0003322327 050,45527

读此卡时,读卡器发送的数据为:00 32 B1 D7 54

下面我们讲解如何进行转换。

参照上面的格式2,将前面 4 个字节00 32 B1 D7当成一个十六进制数字变为数字:0x32B1D7,转换为十进制数字为:3322327,前面补0变为10位数字则正好是卡上标的第一个号码0003322327

参照上面的格式4,将倒数5、6位数字32转换为 3 位十进制数为:050;再将后4位数字B1D7转换为 5 位十进制变成:45527,在计算出的两个数字间加上逗号变成050,45527,正好对应卡片上标注的第二个号码。

编程读取 ID 卡号

编程时碰到一些困难,在第一次读卡时,可以一次性收到 5 个字节,但第二次刷卡时就要分几次才能收到完整数据。惟一确定的是收到的数据长度为5,只能通过这点来保证正确读取卡号了。另外上网也没搜到验证码的算法是什么。没办法,只能直接使用前四个字节的卡号而不验证了。

另外前面我们在讲解《UART 数据通信格式》时说过数据从最低位开始传输(小端传输),此时接收到的前四个数据为00 32 B1 D7。而通过BitConverter.ToUInt32方法将其转化为 32 位无符号整数所得到的结果是3618714112,即十六进制的D7 B1 32 00。这跟接收到的字节正好颠倒,需要将转化出来的整数进行反转操作。即将4个字节的整数的第1个和第4个字节调换,第2和第3个字节调换。

新建一个控制台应用程序,使用如下代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO.Ports;

namespace ConsoleApp2
{
    class Program
    {
        static SerialPort sp = new SerialPort();
        static byte[] cardNum = new byte[5];
        static int index = 0;

        static void Main(string[] args)
        {
            sp.PortName = "COM3";
            sp.BaudRate = 9600;
            sp.Open();

            sp.DataReceived += Sp_DataReceived;

            Console.ReadLine();
        }

        static void Sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (sp.IsOpen)
            {
                byte[] receivedData = new byte[sp.BytesToRead];
                sp.Read(receivedData, 0, receivedData.Length);
                foreach (var num in receivedData)
                {
                    cardNum[index++] = num;
                    if (index == 5)
                    {
                        index = 0;
                        PrintCardNo(); //打印 ID 卡信息
                    }
                }
            }
        }

        static void PrintCardNo()
        {
            //打印10位卡号码
            UInt32 cardNo = BitConverter.ToUInt32(cardNum, 0);
            Console.Write(ReverseBytes(cardNo).ToString().PadLeft(10, '0') + "  ");
            //打印8位卡号码的第一部分
            Console.Write(cardNum[1].ToString().PadLeft(3, '0') + ",");
            //打印8位卡号码的第二部分
            UInt16 part2 = BitConverter.ToUInt16(cardNum, 2);
            Console.WriteLine(ReverseBytes(part2).ToString().PadLeft(5, '0'));
        }
        //翻转32位整数字节序
        public static UInt32 ReverseBytes(UInt32 value)
        {
            return (value & 0x000000FFu) << 24 | (value & 0x0000FF00u) << 8 |
                (value & 0x00FF0000u) >> 8 | (value & 0xFF000000u) >> 24;
        }
        //翻转16位整数字节序
        public static UInt16 ReverseBytes(UInt16 value)
        {
            return (UInt16)((value & 0x00FFu) << 8 | (value & 0xFF00u) >> 8);
        }
    }
}

运行程序,我用了2张卡轮流刷,得到如下结果:

0003322327  050,45527
0002941942  044,58358
0003322327  050,45527
0003322327  050,45527
0002941942  044,58358
0003322327  050,45527

注意:运行本程序之前请注意在【设备管理器】中查看读卡器所使用的端口号是否是 COM3 ,如不是,请更改代码为适合的端口号。

对比程序运行结果与图 2 中的 ID 卡表面标注编号,完全相符。

;

© 2018 - IOT小分队文章发布系统 v0.3